using System;
using NUnit.Framework;
using Tycho.Grammar;
using Tycho.Parser;
using Tycho.Parser.Peg;
using Tycho.Parser.Tokens;
using Tycho.Runtime;

namespace Tycho.UnitTests.Grammar {
    [TestFixture]
    public class TermTranslatorSpecification {
        private readonly TermTranslator Translator = new TermTranslator (i => new SourceLocation (null, "filename", -1, -1, 3, 40));

        [Test]
        public void ShouldTranslateStringTerm () {
            var term = new StringTerm ("value", null);
            AnyObject obj = Translator.Translate (term);

            AssertIsString (obj, "value");
            AssertSourceLocation (obj);
        }

        private void AssertSourceLocation (AnyObject obj) {
            SourceLocation sloc = obj.SourceLocation;
            Assert.That (sloc.FileName, Is.EqualTo("filename"));
            Assert.That (sloc.LineStart, Is.EqualTo(-1));
            Assert.That (sloc.LineEnd, Is.EqualTo(-1));
            Assert.That (sloc.ColumnStart, Is.EqualTo(3));
            Assert.That (sloc.ColumnEnd, Is.EqualTo(40));
        }

        [Test]
        public void ShouldTranslateIntegerTerm () {
            var term = new IntegerTerm (7, null);
            AnyObject obj = Translator.Translate (term);

            AssertIsInteger (obj, 7);
            AssertSourceLocation (obj);
        }

        [Test]
        public void ShouldTranslateFloatingPointTerm () {
            var term = new FloatingPointTerm (7.6, null);
            AnyObject obj = Translator.Translate (term);

            AssertIsReal (obj, 7.6);
            AssertSourceLocation (obj);
        }

        [Test]
        public void ShouldTranslateIdentifierTerm () {
            var term = new IdentifierTerm ("create-yalk", null);
            AnyObject obj = Translator.Translate (term);

            AssertIsIdentifier (obj, "create-yalk");
            AssertSourceLocation (obj);
        }

        [Test]
        public void ShouldTranslateCompositeTerm () {
            var term = new CompositeTerm ("name", null);
            term.Add ("sub1", new IntegerTerm (4, null), false);
            term.Add ("sub2", new IntegerTerm (3, null), false);

            AnyObject obj = Translator.Translate (term);

            Assert.That (obj.GetTermName (), Is.SameAs (Namespaces.Parser.Get ("name")));
            AssertIsInteger (obj.GetProperty (Namespaces.Parser.Get ("sub1")), 4);
            AssertIsInteger (obj.GetProperty (Namespaces.Parser.Get ("sub2")), 3);
            AssertSourceLocation (obj);
        }

        [Test]
        public void ShouldTranslateListTerm () {
            var term = new ListTerm (null);
            term.Terms.Add (new IntegerTerm (4, null));
            term.Terms.Add (new IntegerTerm (3, null));

            AnyObject obj = Translator.Translate (term);

            Assert.That (obj.Count, Is.EqualTo (2));
            AssertIsInteger (obj[0], 4);
            AssertIsInteger (obj[1], 3);
        }

        private void AssertIsString (AnyObject obj, string value) {
            Assert.That (obj.GetTermName (), Is.SameAs (Symbols.ParserString));
            Assert.That (obj.GetProperty (Symbols.ParserString).Equals ((AnyObject) value));
        }

        private void AssertIsInteger (AnyObject obj, int value) {
            Assert.That (obj.GetTermName (), Is.SameAs (Symbols.ParserInteger));
            Assert.That (obj.GetProperty (Symbols.ParserInteger).Equals ((AnyObject) value));
        }

        private void AssertIsIdentifier (AnyObject obj, string name) {
            Assert.That (obj.GetTermName (), Is.SameAs (Symbols.ParserIdentifier));
            Assert.That (obj.GetProperty (Symbols.ParserIdentifier).Equals ((AnyObject) name));
        }

        private void AssertIsReal (AnyObject obj, double value) {
            Assert.That (obj.GetTermName (), Is.SameAs (Symbols.ParserReal));
            Assert.That (obj.GetProperty (Symbols.ParserReal).Equals ((AnyObject) value));
        }
    }
}